package com.stary.mybatisplus3.generator;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.stary.mybatisplus3.exception.MyException;
import com.stary.mybatisplus3.generator.config.DbDriverName;
import com.stary.mybatisplus3.generator.typeconvert.CustomOracleTypeConvert;



/**
 * <p>mybatisplus 3.x 自定义代码生成器</p>
 * 目前只支持 mysql，oracle，sqlserver2005以上版本，默认是oracle
 * @author stary
 * @date 2019年3月19日
 */
public class MyBatisPlusGenerator{
	private static final Logger logger=LoggerFactory.getLogger(MyBatisPlusGenerator.class);
	/*------------------全局配置属性------------------*/
	/**
	 * 全局配置
	 */
	private GlobalConfig globalConfig;
	/**
	 * 开发人员
	 */
	private String author="stary";
	/**
	 * 生成文件的输出目录，默认 D 盘根目录
	 */
	private String outputDir="D:\\";
	/**
	 * 生成的注解带上IdType类型，默认INPUT
	 */
	private IdType idType=IdType.INPUT;
	/**
	 * 是否打开生成后的文件夹 默认 false
	 */
	private boolean open=false;

	/*------------------数据库配置属性------------------*/
	/**
	 * 数据库配置
	 */
	private DataSourceConfig dataSourceConfig;
	/**
	 * 数据库类型，默认 oracle，根据该属性自动匹配驱动名
	 */
	private DbType dbType=DbType.ORACLE;
	/**
	 * 数据库url
	 */
	private String dbUrl;
	/**
	 * 数据库登录用户名
	 */
	private String dbUserName;
	/**
	 * 数据库登录密码
	 */
	private String dbPassword;
	/**
	 * 数据库所在的机器的名称 ，当dbUrl为空时 必填
	 */
	private String dbMachineName;
	/**
	 * 数据库端口号，当dbUrl为空时 必填
	 */
	private String dbPort;
	/**
	 * 数据库名，当dbUrl为空时 必填
	 */
	private String dbName;
	/*------------------生成文件以及实体类其注解配置属性------------------*/
	/**
	 * 策略配置
	 */
	private StrategyConfig strategyConfig;
	/**
	 * 包配置
	 */
	private PackageConfig packageConfig;
	/**
	 * 生成的包路径，默认 com.stary
	 */
	private String packageName="com.stary";
	/**
	 * entity包名，默认model
	 */
	private String entity="model";
	/**
	 * mapper包名，默认 mapper
	 */
	private String mapper="mapper";
	/**
	 * controller包名 默认 controller
	 */
	private String controller="controller";
	/**
	 * 需要生成的表名组，默认生成所有表
	 */
	private String[] tables=null;
	/**
	 * 是否去掉生成实体名的前缀
	 */
	private String[] tablePrefixs = null;
	/**
	 * 是否强制带上实体类属性注解
	 */
	private boolean enableTableFieldAnnotation = true;	
	/**
	 * 是否强制带上序列注解@KeySequence，相关的设置只有orcale才能生效，默认为true 
	 */
	private boolean enableTableSequenceAnnotation = true;
	/**
	 * 自定义设置@KeySequence value 前缀+表名，如 ：value=SEQ_USER
	 */
	private String tableSequenceValuePrefix=null;
	/**
	 * 自定义设置@KeySequence value 表名+后缀，默认_SEQ  如 ：value=USER_SEQ
	 */
	private String tableSequenceValueSuffix="_SEQ";
	/**
	 * 自定义设置@KeySequence clazz 默认Integer，如 clazz=Integer.class  
	 */
	private String tableSequenceClazz="Integer";

	/**
	 * Service是否带'I',默认false 设置成true: user -> IUserService
	 */
	private boolean serviceNameStartWithI=false;
	/*------------------自定义模板以及自定义参数配置------------------*/
	/**
	 * 模板配置
	 */
	private TemplateConfig templateConfig=null;	
	/**
	 * 参数配置
	 */
	private InjectionConfig injectionConfig=null;
	/**
	 * 是否开启模板 默认true
	 */
	private boolean  openTemplate=true;
	/**
	 * 自定义参数
	 */
	private Map<String, Object> params=null;
	
	/**
	 * 执行生成
	 */
	public void execute(){		
		// 自动生成器
		AutoGenerator generator=new AutoGenerator();	
		logger.debug("---------------------->> 加载配置  <<----------------------");
		// 配置生成器
		generator.setGlobalConfig(getGlobalConfig()).setDataSource(getDataSourceConfig())
		.setStrategy(getStrategyConfig()).setPackageInfo(getPackageConfig());
		if(openTemplate){
			generator.setTemplate(getTemplateConfig()).setCfg(getInjectionConfig());
		}
		// 执行生成
		generator.execute();
	}
	
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public String getOutputDir() {
		return outputDir;
	}
	public void setOutputDir(String outputDir) {
		this.outputDir = outputDir;
	}
	public DbType getDbType() {
		return dbType;
	}
	public void setDbType(DbType dbType) {
		// 不是oracle数据库去掉序列注解
		if(!isEmpty(dbType) && !"oracle".equals(dbType.getDb())){			
			enableTableSequenceAnnotation=false;
		}
		this.dbType = dbType;
	}

	public boolean isOpen() {
		return open;
	}
	public void setOpen(boolean open) {
		this.open = open;
	}
	public String getDbUrl() {
		if(isEmpty(dbUrl)){
			if(!isEmpty(dbMachineName) && !isEmpty(dbPort) && !isEmpty(dbName)){
				if("oracle".equals(dbType.getDb())){
					dbUrl="jdbc:oracle:thin:@"+dbMachineName+":"+dbPort+":"+dbName;
				}
				if("mysql".equals(dbType.getDb())){
					dbUrl="jdbc:mysql://"+dbMachineName+":"+dbPort+"/"+dbName;
				}
				if("sql_server".equals(dbType.getDb())){
					dbUrl="jdbc:microsoft:sqlserver://"+dbMachineName+":"+dbPort+";DatabaseName="+dbName;
				}				
			}
		}		
		return dbUrl;
	}
	public void setDbUrl(String dbUrl) {				
		this.dbUrl = dbUrl;
	}
	public String getDbUserName() {
		return dbUserName;
	}
	public void setDbUserName(String dbUserName) {
		this.dbUserName = dbUserName;
	}
	public String getDbPassword() {
		return dbPassword;
	}
	public void setDbPassword(String dbPassword) {
		this.dbPassword = dbPassword;
	}

	public String getDbMachineName() {
		return dbMachineName;
	}
	public void setDbMachineName(String dbMachineName) {
		this.dbMachineName = dbMachineName;
	}
	public String getDbPort() {
		return dbPort;
	}
	public void setDbPort(String dbPort) {
		this.dbPort = dbPort;
	}
	public String getDbName() {
		return dbName;
	}
	public void setDbName(String dbName) {
		this.dbName = dbName;
	}
	public String getPackageName() {
		return packageName;
	}
	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}
	public String getEntity() {
		return entity;
	}
	public void setEntity(String entity) {
		this.entity = entity;
	}
	public String getController() {
		return controller;
	}
	public void setController(String controller) {
		this.controller = controller;
	}
	public String getMapper() {
		return mapper;
	}
	public void setMapper(String mapper) {
		this.mapper = mapper;
	}
	public String[] getTables() {
		return tables;
	}
	public void setTables(String[] tables) {
		this.tables = tables;
	}
	public String[] getTablePrefixs() {
		return tablePrefixs;
	}
	public void setTablePrefixs(String[] tablePrefixs) {
		this.tablePrefixs = tablePrefixs;
	}
	public boolean isEnableTableFieldAnnotation() {
		return enableTableFieldAnnotation;
	}
	public void setEnableTableFieldAnnotation(boolean enableTableFieldAnnotation) {
		this.enableTableFieldAnnotation = enableTableFieldAnnotation;
	}
	public IdType getIdType() {
		return idType;
	}
	public void setIdType(IdType idType) {
		this.idType = idType;
	}
	public boolean isEnableTableSequenceAnnotation() {
		return enableTableSequenceAnnotation;
	}
	public void setEnableTableSequenceAnnotation(
			boolean enableTableSequenceAnnotation) {		
		this.enableTableSequenceAnnotation = enableTableSequenceAnnotation;
	}
	public String getTableSequenceValuePrefix() {
		return tableSequenceValuePrefix;
	}
	public void setTableSequenceValuePrefix(String tableSequenceValuePrefix) {
		this.tableSequenceValuePrefix = tableSequenceValuePrefix;
	}
	public String getTableSequenceValueSuffix() {
		return tableSequenceValueSuffix;
	}
	public void setTableSequenceValueSuffix(String tableSequenceValueSuffix) {
		this.tableSequenceValueSuffix = tableSequenceValueSuffix;
	}
	public String getTableSequenceClazz() {
		return tableSequenceClazz;
	}
	public void setTableSequenceClazz(String tableSequenceClazz) {
		this.tableSequenceClazz = tableSequenceClazz;
	}
	public boolean isServiceNameStartWithI() {
		return serviceNameStartWithI;
	}
	public void setServiceNameStartWithI(boolean serviceNameStartWithI) {
		this.serviceNameStartWithI = serviceNameStartWithI;
	}	

	public boolean isOpenTemplate() {
		return openTemplate;
	}
	public void setOpenTemplate(boolean openTemplate) {
		this.openTemplate = openTemplate;
	}

	public Map<String, Object> getParams() {
		return params;
	}
	public void setParams(Map<String, Object> params) {
		this.params = params;
	}

	public PackageConfig getPackageConfig() {
		logger.debug("---------------------->> 加载包配置  <<----------------------"); 
		if(isEmpty(packageConfig)){
			packageConfig =new PackageConfig();
			logger.debug("父包名---->>{}",packageName);
			logger.debug("entity包名---->>{}",controller);
			logger.debug("mapper包名---->>{}",mapper);
			logger.debug("controller层名---->>{}",controller);
			packageConfig.setParent(packageName); // 父包名
			packageConfig.setController(controller); // controller包名
			packageConfig.setEntity(entity); // entity包名
			packageConfig.setMapper(mapper); // mapper包名
		}
		return packageConfig;
	}
	public GlobalConfig getGlobalConfig() {
		logger.debug("---------------------->> 加载全局配置  <<----------------------"); 
		if(isEmpty(globalConfig)){
			globalConfig=new GlobalConfig();
			logger.debug("开发人员---->>{}",author); 
			logger.debug("生成输出目录---->>{}",outputDir); 
			logger.debug("IdType类型---->>{}",idType); 
			logger.debug("Service是否带'I'---->>{}",serviceNameStartWithI); 
			globalConfig.setActiveRecord(false); // 不需要ActiveRecord特性的请改为false
			globalConfig.setAuthor(author); // 开发人员
			globalConfig.setOutputDir(outputDir); // 生成文件的输出目录，默认 D 盘根目录
			globalConfig.setFileOverride(true); // 是否覆盖已有文件
			globalConfig.setEnableCache(false);// XML 二级缓存
			globalConfig.setBaseResultMap(true);// XML ResultMap
			globalConfig.setBaseColumnList(false);// XML columList
			globalConfig.setOpen(open); // 是否打开输出目录 默认true
			// 开启序列注解 必须要设置 ID类型为 INPUT
			if(enableTableSequenceAnnotation){				
					globalConfig.setIdType(IdType.INPUT);				
			}
			// Service是否带'I'
			if (!serviceNameStartWithI) {
				globalConfig.setServiceName("%sService"); 
			}
			// 自定义文件命名，注意 %s 会自动填充表实体属性！
			// globalConfig.setMapperName("%sDao");
			// globalConfig.setXmlName("%sDao");
			// globalConfig.setServiceName("MP%sService");
			// globalConfig.setServiceImplName("%sServiceDiy");
			// globalConfig.setControllerName("%sAction");
		}
		
		return globalConfig;
	}
	public DataSourceConfig getDataSourceConfig() {
		logger.debug("---------------------->> 加载数据源配置  <<----------------------"); 		
		if(isEmpty(dataSourceConfig)){
			dataSourceConfig=new DataSourceConfig();
			logger.debug("数据库类型---->>{}",dbType.getDb());
			if(isEmpty(getDbUrl())){						
				throw  new  MyException("dataSource url is null");		
			}
			if(isEmpty(dbUserName)){
				throw  new  MyException("dataSource username is null");
			}
			if(isEmpty(dbPassword)){
				throw  new  MyException("dataSource password is null");
			}		
			dataSourceConfig.setDbType(dbType); // 数据库类型
			String driverName=DbDriverName.ORACLE.getName();// 驱动名
			// mysql驱动名
			if(DbType.MYSQL.getDb().equals(dbType.getDb())){
				driverName=DbDriverName.MYSQL.getName();			
			}
			// sqlserver驱动名
			if(DbType.SQL_SERVER.getDb().equals(dbType.getDb())){
				driverName=DbDriverName.SQLSERVER.getName();
			}
			dataSourceConfig.setDriverName(driverName); // 驱动名称
			logger.debug("数据库驱动----->>{}",driverName);
			logger.debug("数据库url----->>{}",getDbUrl());
			dataSourceConfig.setUsername(dbUserName); // 用户名
			dataSourceConfig.setPassword(dbPassword); // 密码
			dataSourceConfig.setUrl(getDbUrl()); // 驱动连接的url	
			dataSourceConfig.setTypeConvert(new CustomOracleTypeConvert());
			
		}
		return dataSourceConfig;
	}
	public StrategyConfig getStrategyConfig() {
		logger.debug("---------------------->> 加载策略配置  <<----------------------"); 
		if(isEmpty(strategyConfig)){
			strategyConfig=new StrategyConfig();
			logger.debug("需要生成的表名---->>{}", arrayToString(tables));
			logger.debug("需要去掉表的前缀---->>{}", arrayToString(tablePrefixs));
			logger.debug("是否生成字段注解---->>{}",enableTableFieldAnnotation); 
			logger.debug("是否生成序列注解---->>{}",enableTableSequenceAnnotation); 		
			if("oracle".equals(dbType.getDb())){
				// ORACLE 注意
				strategyConfig.setCapitalMode(true); //全局大写命名 			
			}
			if(!isEmpty(tablePrefixs)){
				strategyConfig.setTablePrefix(tablePrefixs); // 此处可以修改为您的表前缀	
			}		
			if(!isEmpty(tables)){
				strategyConfig.setInclude(tables); // 需要包含的表名，多个表名传数组	
			}		
			strategyConfig.setNaming(NamingStrategy.underline_to_camel); // 表名生成策略	下划线转驼峰命名		
			strategyConfig.setEntityTableFieldAnnotationEnable(enableTableFieldAnnotation); // 是否生成实体时，生成字段注解	
		}
		return strategyConfig;
	}
	public TemplateConfig getTemplateConfig() {
		logger.debug("---------------------->> 加载自定义模板配置  <<----------------------");
		if(isEmpty(templateConfig)){
			templateConfig=new TemplateConfig();
			templateConfig.setEntity("/templates/my_entity.java");
		}
		return templateConfig;
	}
	public InjectionConfig getInjectionConfig() {
		logger.debug("---------------------->> 加载自定义参数配置  <<----------------------");
		if(isEmpty(injectionConfig)){
			injectionConfig=new InjectionConfig() {
				@Override
				public void initMap() {
					if(isEmpty(params)){
						params=new HashMap<String, Object>();
					}
					params.put("enableTableSequenceAnnotation", enableTableSequenceAnnotation);
					params.put("tableSequenceValuePrefix", tableSequenceValuePrefix);
					params.put("tableSequenceValueSuffix", tableSequenceValueSuffix);
					params.put("tableSequenceClazz", tableSequenceClazz);
					this.setMap(params);
				}
			};	
		}
		return injectionConfig;
	}
	
	public void setGlobalConfig(GlobalConfig globalConfig) {
		this.globalConfig = globalConfig;
	}

	public void setDataSourceConfig(DataSourceConfig dataSourceConfig) {
		this.dataSourceConfig = dataSourceConfig;
	}

	public void setStrategyConfig(StrategyConfig strategyConfig) {
		this.strategyConfig = strategyConfig;
	}

	public void setPackageConfig(PackageConfig packageConfig) {
		this.packageConfig = packageConfig;
	}

	public void setTemplateConfig(TemplateConfig templateConfig) {
		this.templateConfig = templateConfig;
	}

	public void setInjectionConfig(InjectionConfig injectionConfig) {
		this.injectionConfig = injectionConfig;
	}

	/**
	 * 数组转换字符串
	 * @param array
	 * @return
	 */
	private String arrayToString(String[] array){
		StringBuffer buffer=new StringBuffer();
		if(!isEmpty(array)){
			for (int i = 0; i < array.length; i++) {
				buffer.append(array[i]);
				if(i<array.length-1){
					buffer.append(",");
				}
			}
			return buffer.toString();
		}
		return "";
	}
	/**
	 * 判断对象是否为空
	 * @param object
	 * @return
	 */
	private boolean isEmpty(Object object){
		if(object==null || object=="" || object=="null"){
			return true;
		}
		return false;
	}
}
